#define _REENTRANT
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sched.h>
#include "sthread.h"
#include "sync.h"
//	myprog2.c, for project 2

//	created by Seth Simon for group 2 of CIS345

//	The purpose of this program is to test our synchronization to ensure it works
//	in every scenario.

// How to Compile: gcc -m32 -o test myprog2.c sthread.c sync.c

/*	Changes:
			Removed all the fflush(stdout) statements, they're unnecessary
			Added an option for testing read/write_try_lock
			Added tryread10 and trywrite10 functions
			Removed #include <pthread.h> (a remnant of a much earlier version of myprog2)
			Added another write thread in option 5

*/


/*		TEST PROGRAM FOR PROJECT 2
		TEST ALL POSSIBILITIES
		
		REQUIRED NO MATTER WHAT: 1,2,3,4
		FAVOR WRITE THREADS: 5,6
		MULTIPLE READERS: 7
		MISCELLANEOUS: 8
		
		
		
1.			IF A THREAD IS READING, WRITE THREADS SHOULD BE BLOCKED
2.			IF A THREAD IS WRITING, READ THREADS SHOULD BE BLOCKED
3.			IF A THREAD IS WRITING, WRITE THREADS SHOULD BE BLOCKED
4.			READ_TRY_LOCK AND WRITE_TRY_LOCK MUST WORK

5.			IF A THREAD IS READING, AND A WRITE THREAD IS WAITING, READ THREADS SHOULD BE BLOCKED
6.			IF A THREAD IS WRITING AND A READ THREAD AND A WRITE THREAD ARE WAITING,
			THE WRITE THREAD SHOULD BE PERMITTED ACCESS BEFORE THE READ THREAD IN ALL CASES
			
7.			IF A THREAD IS READING AND NO WRITERS ARE WAITING, READ THREADS SHOULD NOT BE BLOCKED

8.			CREATE A CUSTOM AMOUNT AND TYPE OF THREADS
			
*/

enum boolean{FALSE,TRUE};

int balance;
sthread_rwlock_t mylock;

int tryread10(void *arg) {
	int threadno = (int) arg;
	printf("%d: Trying to get read lock...\n",threadno);
	if(sthread_read_try_lock(&mylock) == 1) {
		printf("%d: Failed to get read lock\n",threadno);
	}
	else {
		printf("%d: Got lock, reading...\n",threadno);
		sleep(5);
		printf("%d: Balance is %d\n",threadno,balance);
		sleep (5);
		printf("%d: Read done\n",threadno);
		sthread_read_unlock(&mylock);
		printf("%d: Read has been unlocked\n",threadno);
	}
	return 0;
}

int trywrite10(void *arg) {
	int threadno = (int) arg;
	printf("%d: Trying to get write lock...\n",threadno);
	if(sthread_write_try_lock(&mylock) == 1) {
		printf("%d: Failed to get write lock\n",threadno);
	}
	else {
		printf("%d: Got lock, writing...\n",threadno);
		sleep(5);
		printf("%d: Balance incremented by 1 to %d\n",threadno,++balance);
		sleep(5);
		printf("%d: Write done\n",threadno);
		sthread_write_unlock(&mylock);
		printf("%d: Write has been unlocked\n",threadno);
	}
	return 0;
}
		

int read10(void *arg) {
	int threadno = (int) arg;
	printf("%d: Obtaining read lock...\n",threadno);
	sthread_read_lock(&mylock);
	printf("%d: Got lock, reading...\n",threadno);
	sleep(5);
	printf("%d: Balance is %d\n",threadno,balance);
	sleep (5);
	printf("%d: Read done\n",threadno);
	sthread_read_unlock(&mylock);
	printf("%d: Read has been unlocked\n",threadno);
	return 0;
}

int write10(void *arg) {
	int threadno = (int) arg;
	printf("%d: Obtaining write lock...\n",threadno);
	sthread_write_lock(&mylock);
	printf("%d: Got lock, writing...\n",threadno);
	sleep(5);
	printf("%d: Balance incremented by 1 to %d\n",threadno,++balance);
	sleep(5);
	printf("%d: Write done\n",threadno);
	sthread_write_unlock(&mylock);
	printf("%d: Write has been unlocked\n",threadno);
	return 0;
}
	

int main(int argc, char **argv) {
	printf("POSSIBILITIES:\n");
	printf("\tREQUIRED:\n");
	printf("\t1. If a thread is reading, write threads should be blocked.\n");
	printf("\t2. If a thread is writing, read threads should be blocked.\n");
	printf("\t3. If a thread is writing, write threads should be blocked.\n");
	printf("\t4. Read_try_lock and write_try lock must work.\n");
	
	printf("\n\tFAVOR WRITE THREADS:\n");
	printf("\t5. If a thread is reading and a write thread is waiting, read threads should be blocked.\n");
	printf("\t6. If a thread is writing, and read and write threads are waiting, the thread \
	to gain access next must be a write thread.\n");
	
	printf("\n\tMULTIPLE READERS:\n");
	printf("\t7. If a thread is reading and no write threads are waiting, readers should not be blocked.\n");
	
	printf("\n\tMISCELLANEOUS:\n");
	printf("\t8: Create a custom amount and type of threads to ensure they work correctly.\n");
	
	int option = 0;
	while(option < 1 || option > 8) {
		printf("\nChoose which option to test (1-8): ");
		char str[100];
		fgets(str,99,stdin);
		str[strlen(str) - 1] = '\0'; //replace \n with \0
		option = atoi(str);
	}
	
	sthread_init();
	balance = 1000;
	sthread_rwlock_init(&mylock);
	
	if(option == 1) {
		sthread_t thread0,thread1;
		sthread_create(&thread0,read10,(void *)0);
		sleep(1); //ensure read thread starts before write thread...I had this problem before adding this statement
		sthread_create(&thread1,write10,(void *)1);
		sleep(30); //ensure main finishes AFTER the threads finish
	}
	else if(option == 2) {
		sthread_t thread0,thread1;
		sthread_create(&thread0,write10,(void *)0);
		sleep(1);
		sthread_create(&thread1,read10,(void *)1);
		sleep(30);
	}
	else if(option == 3) {
		sthread_t thread0,thread1;
		sthread_create(&thread0,write10,(void *)0);
		sleep(1);
		sthread_create(&thread1,write10,(void *)1);
		sleep(30);
	}
	else if(option == 4) {
		sthread_t thread0,thread1,thread2,thread3;
		sthread_create(&thread0,trywrite10,(void *)0); //try_write_lock returns 0 (got the lock)
		sleep(1);
		sthread_create(&thread1,tryread10,(void *)1); // try_read_lock returns 1 (didn't get lock)
		sleep(10);
		sthread_create(&thread2,tryread10,(void *)2); //try_read_lock returns 0 (got lock)
		sleep(1);
		sthread_create(&thread3,trywrite10,(void *)3); // try_write_lock returns 1 (didn't get lock)
		sleep(10);
	}
	else if(option == 5) {
		sthread_t thread0,thread1,thread2,thread3;
		sthread_create(&thread0,read10,(void *)0);
		sleep(1);
		sthread_create(&thread1,write10,(void *)1);
		sleep(1);
		sthread_create(&thread2,read10,(void *)2);
		sleep(1);
		sthread_create(&thread3,write10,(void *)3);
		sleep(40);
	}
	else if(option == 6) {
		sthread_t thread0,thread1,thread2;
		sthread_create(&thread0,write10,(void *)0);
		sleep(1);
		sthread_create(&thread1,read10,(void *)1);
		sleep(1);
		sthread_create(&thread2,write10,(void *)2);
		sleep(40);
	}
	else if(option == 7) {
		sthread_t thread0,thread1;
		sthread_create(&thread0,read10,(void *)0);
		sleep(1);
		sthread_create(&thread1,read10,(void *)1);
		sleep(30);
	}
	else if(option == 8) {
		int inputOkay = FALSE;
		char types[1000];
		while(!inputOkay) {
			printf("How many and what kind? (e.g. \"rrw\" will create, in that order, a read thread, another read thread, and a write thread\n");
			printf("Enter a string of r's and w's: ");
			scanf("%s",types);
			inputOkay = TRUE;
			int i;
			for(i=0; i<strlen(types);i++) {
				if(types[i] != 'r' && types[i] != 'w') {
					inputOkay = FALSE;
				}
			}
		}
		
		sthread_t threads[strlen(types)];
		int j;
		for(j=0;j<strlen(types);j++) {
			if(types[j] == 'r') {
				sthread_create(&threads[j],read10,(void *)j);
				sleep(1);
			}
			else {
				sthread_create(&threads[j],write10,(void *)j);
				sleep(1);
			}
		}
		sleep(12*strlen(types));
	} //end if option==7
		
	
}
	
	
	

